package com.codeplatform.util;

import com.codeplatform.dto.CodeExecutionResult;
import com.codeplatform.service.ResourceCleanupService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.*;

/**
 * Docker代码执行器
 * 使用Docker容器安全执行用户代码
 */
@Slf4j
@Component
public class DockerExecutor {
    
    private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/code-execution/";
    private static final ExecutorService executorService = Executors.newCachedThreadPool();
    
    @Autowired
    private TimeoutManager timeoutManager;
    
    @Autowired
    private ResourceCleanupService resourceCleanupService;
    
    /**
     * 执行代码
     */
    public CodeExecutionResult executeCode(String dockerImage, String command, String fileName, 
                                         String code, String input, int timeoutSeconds, int memoryLimitMB) {
        return executeCodeWithRealTimeOutput(dockerImage, command, fileName, code, input, 
                                           timeoutSeconds, memoryLimitMB, null);
    }
    
    /**
     * 执行代码（支持实时输出）
     * 
     * @param dockerImage Docker镜像
     * @param command 执行命令
     * @param fileName 文件名
     * @param code 代码内容
     * @param input 输入数据
     * @param timeoutSeconds 超时时间（秒）
     * @param memoryLimitMB 内存限制（MB）
     * @param outputCallback 实时输出回调函数
     * @return 执行结果
     */
    public CodeExecutionResult executeCodeWithRealTimeOutput(String dockerImage, String command, String fileName, 
                                                           String code, String input, int timeoutSeconds, 
                                                           int memoryLimitMB, java.util.function.Consumer<String> outputCallback) {
        return executeCodeWithRealTimeOutput(dockerImage, command, fileName, code, input, 
                                           timeoutSeconds, memoryLimitMB, outputCallback, "unknown");
    }
    
    /**
     * 执行代码（支持实时输出和语言优化）
     * 
     * @param dockerImage Docker镜像
     * @param command 执行命令
     * @param fileName 文件名
     * @param code 代码内容
     * @param input 输入数据
     * @param timeoutSeconds 超时时间（秒）
     * @param memoryLimitMB 内存限制（MB）
     * @param outputCallback 实时输出回调函数
     * @param language 编程语言
     * @return 执行结果
     */
    public CodeExecutionResult executeCodeWithRealTimeOutput(String dockerImage, String command, String fileName, 
                                                           String code, String input, int timeoutSeconds, 
                                                           int memoryLimitMB, java.util.function.Consumer<String> outputCallback,
                                                           String language) {
        
        String sessionId = generateSessionId();
        Path workDir = null;
        long startTime = System.currentTimeMillis();
        
        // 注册执行会话到资源清理服务
        ResourceCleanupService.ExecutionSession session = resourceCleanupService.registerSession(sessionId, language);
        
        // 使用TimeoutManager获取有效的超时时间，考虑代码长度
        int codeLength = code != null ? code.length() : 0;
        int effectiveTimeout = timeoutManager.getEffectiveTimeout(language, timeoutSeconds, codeLength);
        log.debug("语言: {}, 代码长度: {}, 请求超时: {}s, 有效超时: {}s", 
                  language, codeLength, timeoutSeconds, effectiveTimeout);
        
        try {
            // 1. 创建临时工作目录
            workDir = createWorkDirectory(sessionId);
            resourceCleanupService.updateSessionWorkDirectory(sessionId, workDir);
            
            // 2. 写入代码文件
            writeCodeFile(workDir, fileName, code);
            
            // 3. 写入输入文件（如果有）
            if (input != null && !input.trim().isEmpty()) {
                writeInputFile(workDir, input);
            }
            
            // 4. 构建Docker命令
            String dockerCommand = buildDockerCommand(dockerImage, command, fileName, 
                                                    workDir.toString(), effectiveTimeout, memoryLimitMB);
            
            // 5. 使用TimeoutManager执行Docker命令
            final Path finalWorkDir = workDir;
            return timeoutManager.executeWithTimeout(
                sessionId,
                () -> executeDockerCommandInternal(dockerCommand, effectiveTimeout, outputCallback),
                effectiveTimeout,
                null
            );
            
        } catch (RuntimeException e) {
            long actualTime = System.currentTimeMillis() - startTime;
            if (e.getMessage().contains("执行超时")) {
                log.warn("代码执行超时，会话ID: {}, 语言: {}, 超时时间: {}s, 实际时间: {}ms", 
                    sessionId, language, effectiveTimeout, actualTime);
                // 标记会话超时，触发资源清理
                resourceCleanupService.markSessionTimeout(sessionId, actualTime);
                return timeoutManager.createTimeoutResult(effectiveTimeout, actualTime);
            }
            log.error("代码执行异常: {}", e.getMessage(), e);
            // 清理异常会话的资源
            resourceCleanupService.cleanupSessionResources(sessionId, "执行异常清理");
            return CodeExecutionResult.failure("执行异常: " + e.getMessage(), 0, "系统错误");
        } catch (Exception e) {
            log.error("代码执行异常: {}", e.getMessage(), e);
            // 清理异常会话的资源
            resourceCleanupService.cleanupSessionResources(sessionId, "执行异常清理");
            return CodeExecutionResult.failure("执行异常: " + e.getMessage(), 0, "系统错误");
        } finally {
            // 6. 清理临时文件（保留原有逻辑作为备份）
            if (workDir != null) {
                cleanupWorkDirectory(workDir);
            }
        }
    }
    
    /**
     * 语法检查
     */
    public CodeExecutionResult validateSyntax(String dockerImage, String command, String fileName, String code) {
        String sessionId = generateSessionId();
        Path workDir = null;
        
        try {
            workDir = createWorkDirectory(sessionId);
            writeCodeFile(workDir, fileName, code);
            
            // 对于编译型语言，只进行编译检查
            String compileCommand = getCompileCommand(command);
            String dockerCommand = buildDockerCommand(dockerImage, compileCommand, fileName, 
                                                    workDir.toString(), 10, 128);
            
            CodeExecutionResult result = executeDockerCommand(dockerCommand, 10);
            
            if (result.getSuccess()) {
                result.setStatusMessage("语法检查通过");
            } else {
                result.setStatusMessage("语法错误");
                result.setStatus(4); // 编译错误
            }
            
            return result;
            
        } catch (Exception e) {
            log.error("语法检查异常: {}", e.getMessage(), e);
            return CodeExecutionResult.failure("语法检查异常: " + e.getMessage(), 0, "检查失败");
        } finally {
            if (workDir != null) {
                cleanupWorkDirectory(workDir);
            }
        }
    }
    
    /**
     * 快速语法检查（解释型语言）
     */
    public CodeExecutionResult quickSyntaxCheck(String dockerImage, String command, String fileName, String code) {
        // 对于解释型语言，使用语法检查参数
        String syntaxCommand = getSyntaxCheckCommand(command, fileName);
        return validateSyntax(dockerImage, syntaxCommand, fileName, code);
    }
    
    /**
     * 生成会话ID
     */
    private String generateSessionId() {
        return "exec_" + System.currentTimeMillis() + "_" + Thread.currentThread().getId();
    }
    
    /**
     * 从Docker命令中提取会话ID
     * 
     * @param dockerCommand Docker命令
     * @return 会话ID
     */
    private String extractSessionIdFromCommand(String dockerCommand) {
        try {
            // 从工作目录路径中提取会话ID
            if (dockerCommand.contains("/code-execution/")) {
                String[] parts = dockerCommand.split("/code-execution/");
                if (parts.length > 1) {
                    String workDirPart = parts[1];
                    String[] pathParts = workDirPart.split(":");
                    if (pathParts.length > 0) {
                        return pathParts[0];
                    }
                }
            }
        } catch (Exception e) {
            log.warn("提取会话ID失败: {}", e.getMessage());
        }
        return null;
    }
    
    /**
     * 获取最新运行的容器ID
     * 
     * @return 容器ID
     */
    private String getRunningContainerId() {
        try {
            // 获取最新创建的容器ID
            ProcessBuilder pb = new ProcessBuilder("docker", "ps", "-l", "-q");
            Process process = pb.start();
            
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                String containerId = reader.readLine();
                if (containerId != null && !containerId.trim().isEmpty()) {
                    return containerId.trim();
                }
            }
            
            process.waitFor();
        } catch (Exception e) {
            log.warn("获取容器ID失败: {}", e.getMessage());
        }
        return null;
    }
    
    /**
     * 创建工作目录
     */
    private Path createWorkDirectory(String sessionId) throws IOException {
        Path workDir = Paths.get(TEMP_DIR, sessionId);
        Files.createDirectories(workDir);
        return workDir;
    }
    
    /**
     * 写入代码文件
     */
    private void writeCodeFile(Path workDir, String fileName, String code) throws IOException {
        Path codeFile = workDir.resolve(fileName);
        Files.write(codeFile, code.getBytes("UTF-8"));
    }
    
    /**
     * 写入输入文件
     */
    private void writeInputFile(Path workDir, String input) throws IOException {
        Path inputFile = workDir.resolve("input.txt");
        Files.write(inputFile, input.getBytes("UTF-8"));
    }
    
    /**
     * 构建Docker命令（加强安全沙箱隔离）
     */
    private String buildDockerCommand(String dockerImage, String command, String fileName, 
                                    String workDir, int timeoutSeconds, int memoryLimitMB) {
        
        StringBuilder cmd = new StringBuilder();
        cmd.append("docker run ");
        
        // 容器标签（用于资源清理识别）
        cmd.append("--label code-platform=true ");
        cmd.append("--label session-id=").append(extractSessionIdFromCommand(command)).append(" ");
        
        // 资源限制
        cmd.append("--memory=").append(memoryLimitMB).append("m ");
        cmd.append("--memory-swap=").append(memoryLimitMB).append("m "); // 禁用swap
        cmd.append("--cpus=0.5 ");
        cmd.append("--cpu-shares=512 "); // CPU权重限制
        cmd.append("--pids-limit=50 "); // 进程数限制
        
        // 网络隔离
        cmd.append("--network=none ");
        cmd.append("--dns=127.0.0.1 "); // 禁用DNS解析
        
        // 文件系统安全
        cmd.append("--read-only ");
        cmd.append("--tmpfs /tmp:rw,noexec,nosuid,size=50m ");
        cmd.append("--tmpfs /var/tmp:rw,noexec,nosuid,size=10m ");
        
        // 用户权限控制
        cmd.append("--user 1000:1000 ");
        cmd.append("--group-add nogroup ");
        
        // 安全选项
        cmd.append("--security-opt=no-new-privileges ");
        cmd.append("--cap-drop=ALL ");
        cmd.append("--cap-add=SETUID ");
        cmd.append("--cap-add=SETGID ");
        
        // 系统调用限制
        cmd.append("--security-opt=seccomp=default ");
        
        // 设备访问限制
        cmd.append("--device-read-bps /dev/sda:1mb ");
        cmd.append("--device-write-bps /dev/sda:1mb ");
        
        // 挂载点
        cmd.append("-v \"").append(workDir).append(":/workspace:rw,noexec\" ");
        cmd.append("-w /workspace ");
        
        // 环境变量限制
        cmd.append("-e HOME=/tmp ");
        cmd.append("-e PATH=/usr/local/bin:/usr/bin:/bin ");
        cmd.append("-e LANG=C.UTF-8 ");
        
        // 执行超时（通过timeout命令）
        cmd.append("--timeout ").append(timeoutSeconds).append("s ");
        cmd.append(dockerImage).append(" ");
        cmd.append("timeout ").append(timeoutSeconds).append("s ");
        cmd.append("sh -c \"");
        
        // 添加资源监控和限制
        cmd.append("ulimit -t ").append(timeoutSeconds).append(" && ");
        cmd.append("ulimit -f 10240 && "); // 文件大小限制10MB
        cmd.append("ulimit -v ").append(memoryLimitMB * 1024).append(" && "); // 虚拟内存限制
        
        // 如果有输入文件，重定向输入
        if (Files.exists(Paths.get(workDir, "input.txt"))) {
            cmd.append(command).append(" < input.txt");
        } else {
            cmd.append(command);
        }
        
        cmd.append("\"");
        
        return cmd.toString();
    }
    
    /**
     * 执行Docker命令（兼容旧接口）
     */
    private CodeExecutionResult executeDockerCommand(String dockerCommand, int timeoutSeconds) {
        return executeDockerCommandInternal(dockerCommand, timeoutSeconds, null);
    }
    
    /**
     * 执行Docker命令（内部方法，支持实时输出）
     * 
     * @param dockerCommand Docker命令
     * @param timeoutSeconds 超时时间
     * @param outputCallback 实时输出回调函数
     * @return 执行结果
     */
    private CodeExecutionResult executeDockerCommandInternal(String dockerCommand, int timeoutSeconds, 
                                                           java.util.function.Consumer<String> outputCallback) {
        long startTime = System.currentTimeMillis();
        String containerId = null;
        String sessionId = extractSessionIdFromCommand(dockerCommand);
        
        try {
            // 启动Docker容器并获取容器ID
            Process process = Runtime.getRuntime().exec(dockerCommand);
            
            // 获取容器ID（通过docker ps命令）
            containerId = getRunningContainerId();
            if (containerId != null && sessionId != null) {
                resourceCleanupService.updateSessionContainer(sessionId, containerId);
            }
            
            // 跟踪进程
            if (sessionId != null) {
                resourceCleanupService.updateSessionProcess(sessionId, process);
            }
            
            StringBuilder outputBuilder = new StringBuilder();
            StringBuilder errorBuilder = new StringBuilder();
            
            // 创建线程来实时读取输出
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getInputStream(), "UTF-8"))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        outputBuilder.append(line).append("\n");
                        // 实时输出回调
                        if (outputCallback != null) {
                            outputCallback.accept(line + "\n");
                        }
                    }
                } catch (IOException e) {
                    log.warn("读取输出流异常: {}", e.getMessage());
                }
            });
            
            Thread errorThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getErrorStream(), "UTF-8"))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        errorBuilder.append(line).append("\n");
                        // 错误输出也通过回调发送
                        if (outputCallback != null) {
                            outputCallback.accept("[ERROR] " + line + "\n");
                        }
                    }
                } catch (IOException e) {
                    log.warn("读取错误流异常: {}", e.getMessage());
                }
            });
            
            outputThread.start();
            errorThread.start();
            
            // 等待进程完成（超时由TimeoutManager处理）
            int exitCode = process.waitFor();
            
            // 等待输出线程完成
            outputThread.join(2000);
            errorThread.join(2000);
            
            long executionTime = System.currentTimeMillis() - startTime;
            String output = outputBuilder.toString().trim();
            String error = errorBuilder.toString().trim();
            
            if (exitCode == 0) {
                return CodeExecutionResult.success(output, executionTime, 0L);
            } else {
                return CodeExecutionResult.failure(error.isEmpty() ? "程序异常退出" : error, 
                                                 exitCode, "运行时错误");
            }
            
        } catch (Exception e) {
            log.error("Docker命令执行异常: {}", e.getMessage(), e);
            return CodeExecutionResult.failure("Docker执行异常: " + e.getMessage(), 0, "系统错误");
        }
    }
    
    /**
     * 读取流内容
     */
    private String readStream(InputStream inputStream) throws IOException {
        StringBuilder result = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {
            String line;
            while ((line = reader.readLine()) != null) {
                result.append(line).append("\n");
            }
        }
        return result.toString().trim();
    }
    
    /**
     * 获取编译命令
     */
    private String getCompileCommand(String fullCommand) {
        // 提取编译部分（&&之前的部分）
        if (fullCommand.contains("&&")) {
            return fullCommand.split("&&")[0].trim();
        }
        return fullCommand;
    }
    
    /**
     * 获取语法检查命令
     */
    private String getSyntaxCheckCommand(String command, String fileName) {
        if (command.contains("python")) {
            return "python -m py_compile " + fileName;
        } else if (command.contains("node")) {
            return "node --check " + fileName;
        } else if (command.contains("go")) {
            return "go fmt " + fileName + " && go vet " + fileName;
        }
        return command;
    }
    
    /**
     * 清理工作目录
     */
    private void cleanupWorkDirectory(Path workDir) {
        try {
            Files.walk(workDir)
                .sorted((a, b) -> b.compareTo(a)) // 先删除文件，再删除目录
                .forEach(path -> {
                    try {
                        Files.deleteIfExists(path);
                    } catch (IOException e) {
                        log.warn("清理文件失败: {}", path, e);
                    }
                });
        } catch (Exception e) {
            log.warn("清理工作目录失败: {}", workDir, e);
        }
    }
}